//=============================================================================
//
//   Copyright (C) 2020 All rights reserved.
//
//   Filename:		gauss_point.rs
//
//   Author: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Date: 2020-10-07 00:11
//
//   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Last modified:	2020-11-06 20:41
//
//   Description:
//
//=============================================================================
use crate::dimension::{DimType, HasDimension, PlaneStrain, PlaneStress, ThreeDimension};
use crate::geometry::point::PointLike;
use crate::util::{record::Record, Indexable};
use crate::Real;
use nalgebra::{allocator::Allocator, dimension::Dim, DefaultAllocator};

/// struct gauss point
#[derive(Clone, Debug)]
pub struct GaussPoint<DT: DimType> {
    id: Option<usize>,
    coords: nalgebra::Point3<Real>,
    weight: Real,
    mat: Option<usize>,
    rec: Record<DT>,
}

/// trait gauss point like
pub trait GaussPointLike<DT: DimType> {
    fn get_weight(&self) -> Real;
    fn set_weight(&mut self, rhs: Real);
}
/// default constructor
impl<DT: DimType> Default for GaussPoint<DT> {
    #[inline]
    fn default() -> Self {
        Self {
            id: None,
            coords: nalgebra::Point3::origin(),
            weight: 0 as Real,
            mat: None,
            rec: Record::<DT>::default(),
        }
    }
}
impl<DT: DimType> HasDimension for GaussPoint<DT> {
    fn get_dim(&self) -> usize {
        DT::Dim::try_to_usize().expect("Failed to usize dimension")
    }
    fn get_topo_dim(&self) -> usize {
        0usize
    }
}
impl<DT: DimType> PointLike<<DT as DimType>::Dim> for GaussPoint<DT> {
    #[inline]
    fn get_coord(&self) -> nalgebra::VectorN<Real, <DT as DimType>::Dim>
    where
        DefaultAllocator: Allocator<Real, <DT as DimType>::Dim>,
    {
        self.coords
            .coords
            .fixed_rows::<<DT as DimType>::Dim>(0)
            .clone_owned()
    }
    #[inline]
    fn as_np(&self) -> &nalgebra::Point3<Real> {
        &self.coords
    }
    #[inline]
    fn as_np_mut(&mut self) -> &mut nalgebra::Point3<Real> {
        &mut self.coords
    }
}

/// constructor
impl GaussPoint<PlaneStrain> {
    #[inline]
    pub fn new(x: Real, y: Real, weight: Real) -> Self {
        Self {
            coords: nalgebra::Point3::<Real>::new(x, y, 0 as Real),
            weight,
            mat: None,
            ..Default::default()
        }
    }
}
impl GaussPoint<PlaneStress> {
    #[inline]
    pub fn new(x: Real, y: Real, weight: Real) -> Self {
        Self {
            coords: nalgebra::Point3::<Real>::new(x, y, 0 as Real),
            weight,
            mat: None,
            ..Default::default()
        }
    }
}
impl GaussPoint<ThreeDimension> {
    #[inline]
    pub fn new(x: Real, y: Real, z: Real, weight: Real) -> Self {
        Self {
            coords: nalgebra::Point3::<Real>::new(x, y, z),
            weight,
            mat: None,
            ..Default::default()
        }
    }
}

gauss_point_like_impl!(GaussPoint<DT: DimType>);
indexable_impl!(GaussPoint<DT: DimType>);
has_material_impl!(GaussPoint<DT: DimType>);

///
/// test
///
#[test]
fn constructor() {
    let gp1 = GaussPoint::<PlaneStress>::default();
    assert_eq!(gp1.as_np().x, 0f32);
    assert_eq!(gp1.as_np().y, 0f32);
    let mut gp2 = GaussPoint::<PlaneStress>::new(1f32, 2f32, 10f32);
    assert_eq!(gp2.as_np().x, 1f32);
    assert_eq!(gp2.as_np().y, 2f32);
    assert_eq!(gp2.get_weight(), 10f32);
    gp2.set_index(43);
    assert_eq!(gp2.get_index().unwrap(), 43);
}
// #[test]
// fn has_material() {
//     use crate::material::elastic::Elastic;
//     let mut gp = GaussPoint::<PlaneStress>::default();
//     let mat: Elastic<PlaneStress> = serde_json::from_str(
//         r#"
//         {
//             "id": 2,
//             "type": "elastic",
//             "volume_weight": 19,
//             "friction_angle": 30,
//             "dilation_angle": 30,
//             "cohesion": 10,
//             "permeability": [1e-5,1e-5, 1e-5 ],
//             "zeta": 0.1,
//             "K_IC": 2.9,
//             "elastic_modulus": 1000000.0,
//             "elastic_poisson": 0.3
//         }"#,
//     )
//     .unwrap();
//     let sp_mat = Rc::new(mat) as Rc<dyn MaterialLike<PlaneStress>>;
//     gp.set_material(Rc::downgrade(&sp_mat));
//     assert_eq!(gp.get_material().get_volume_weight(), 19 as Real);
//     assert_eq!(gp.get_material().get_volume_weight(), 19 as Real);
// }
